
/*
 * 
 * @author Nebojsa Trninic
 * @date   10.09.2012.
 *
 * Examples of usage of Invoker of Background Commands, i.e. IBC.
 *
 */


#include "ibc.h"

using namespace IBC;
using namespace std;


// Very simple command CMyCommand1.
class CMyCommand1 : public ICommand
{
    virtual void execute( void );

}; // class CMyCommand1


void CMyCommand1::execute( void )
{
    cout << "CMyCommand1::execute()" << endl;
    // No exception handling in this one can cause problems, since it should catch exceptions.

} // CMyCommand1::execute()


// More complex command CMyCommand2div that accepts parameters and can be used as an example
// of different outcomes of operation execution: OK, ERR, EXCeption and TimeOut.
// Sleep time m_nST [seconds] introduced in order to be able to show timeout behaviour.
class CMyCommand2div : public ICommand
{
  public:
    CMyCommand2div( unsigned long v_nST ) : m_fA( 0.0 ), m_fB( 0.0 ),
                                            m_fRes( 0.0 ), m_nST( v_nST )
        {
            // state INIT from parent constructor
        }; // constructor CMyCommand2div()

    void setParams( float v_fA, float v_fB );

    virtual void execute( void ); // Calculates A/B. Does exception handling.

    float getResult( void ); // User should check state of command and only if S_EXEC_OK call this method.

  private:
    // Parameters
    float m_fA;
    float m_fB;
    unsigned long m_nST; // Sleep time in seconds. execute() is going to sleep it out.

    // Result/Error/Exception
    float m_fRes;
    // For the purpose of error, we are going to mark 24.0 / 3.0 = 8.0 as error.
    // Exception is handled in standard way in ICommand, execute() should allocate it. Division by
    // zero can be used as an example.

}; // CMyCommand2div



void CMyCommand2div::setParams( float v_fA, float v_fB )
{
    assert( m_nCommandState == CS_S_INIT );
            
    m_fA = v_fA;
    m_fB = v_fB;

    m_nCommandState = CS_S_PARAMSSET;

} // CMyCommand2div::setParams()


class CExceptionMyDiv0 : public exception
{
  virtual const char* what() const throw()
  {
    return "My Div by 0 exception happened";
  }

}; // class CExceptionMyDiv0


void CMyCommand2div::execute( void )
{
    try
    {
        cout << "CMyCommand2div::execute() begin" << endl;
        // More complex commands could check if parameters have been set and throw exception if not, or
        // mark it as an error.
        assert( m_nCommandState == CS_S_PARAMSSET );

        // For the purpose of timeout demo, sleep has been introduced.
        CThread::sleep( m_nST, 0 );
        
        // Perform calculation.
        m_fRes = m_fA / m_fB;

        cout << "******* " << m_fA << " / " << m_fB << " = " << m_fRes << endl;

        // Since debugging showed that 1.0 / 0.0 = inf without an exception, we raise one in need of it.
        if( m_fB == 0.0 )
        {
            CExceptionMyDiv0 eobj;
            throw eobj;
        }

        if( m_fRes == 8.0 ) // E.g. 24.0 / 3.0 = 8.0
            m_nCommandState = CS_E_EXEC_ERR; // Internal decision to mark it as an error for demo purposes.
        else
            m_nCommandState = CS_S_EXEC_OK; // Demo of good results.
    }
    catch( exception &e )
    {
        cerr << "exception caught: " << e.what() << endl;
        if( m_pExc == NULL )
        {
            m_pExc = new CExceptionMyDiv0;   // This one is released in command destructor.
            m_nCommandState = CS_E_EXEC_EXC; // Div by 0 can be demo of exception behaviour.
        }
    } // catch

    cout << "CMyCommand2div::execute() end" << endl;

} // CMyCommand2div::execute()


float CMyCommand2div::getResult( void )
{
    assert( m_nCommandState == CS_S_EXEC_OK ); // Other commands may decide differently
                                            // and allow this method also in case of error.

    return m_fRes;

} // CMyCommand2div::getResult()


int main( int arhc, char *argv[] )
{
    cout << "******* IBC start" << endl;

    cout << "******* Simple command demo" << endl;
    {
        ICommand *pCmd1 = new CMyCommand1;
        CInvokerBackgroundCommands *pInv1 = new CInvokerBackgroundCommands( 1, // 1s timeout.
                                                                            CInvokerBackgroundCommands::TOP_LINGER,
                                                                            pCmd1 );

        assert( pInv1->getInvokerState() == CInvokerBackgroundCommands::IS_S_PENDING );

        pInv1->execCmdInBgThr();

        assert( pInv1->getInvokerState() == CInvokerBackgroundCommands::IS_S_DONE );
        assert( pInv1->RCget() == 1 ); // only Invoker referencing, Bg thread is gone by now.

        pInv1->RCdec(); // resource deallocation follows.

    } // simple


    cout << "******* More complex command demo OK" << endl;
    {
        CMyCommand2div *pCmd2 = new CMyCommand2div( 0 ); // 99 * 99 = 9801, do not sleep.
        pCmd2->setParams( 9801.0, 99.0 );

        CInvokerBackgroundCommands *pInv2 = new CInvokerBackgroundCommands( 1, // 1s timeout.
                                                                            CInvokerBackgroundCommands::TOP_LINGER,
                                                                            pCmd2 );

        assert( pInv2->getInvokerState() == CInvokerBackgroundCommands::IS_S_PENDING );

        pInv2->execCmdInBgThr();

        assert( pInv2->getInvokerState() == CInvokerBackgroundCommands::IS_S_DONE );
        assert( pInv2->RCget() == 1 ); // only Invoker referencing, Bg thread is gone by now. Safe to access
                                       // Invoker command without locking, i.e. pCmd2.
        assert( pCmd2->getException() == NULL );
        assert( pCmd2->getCommandState() == ICommand::CS_S_EXEC_OK );

        float fRes = pCmd2->getResult();
        assert( fRes == 99.0 );

        cout << "******* Command 2 result is " << fRes << endl;

        pInv2->RCdec(); // resource deallocation follows.

    } // complex OK

    cout << "******* More complex command demo ERROR" << endl;
    {
        CMyCommand2div *pCmd2 = new CMyCommand2div( 0 ); // class is such that 24.0 / 3.0 = 8.0 is error.
        pCmd2->setParams( 24.0, 3.0 );

        CInvokerBackgroundCommands *pInv2 = new CInvokerBackgroundCommands( 1, // timeout 1s
                                                                            CInvokerBackgroundCommands::TOP_LINGER,
                                                                            pCmd2 );

        assert( pInv2->getInvokerState() == CInvokerBackgroundCommands::IS_S_PENDING );

        pInv2->execCmdInBgThr();

        assert( pInv2->getInvokerState() == CInvokerBackgroundCommands::IS_S_DONE );
        assert( pInv2->RCget() == 1 ); // only Invoker referencing, Bg thread is gone by now. Safe to access
                                       // Invoker command without locking, i.e. pCmd2.
        assert( pCmd2->getException() == NULL );
        assert( pCmd2->getCommandState() == ICommand::CS_E_EXEC_ERR );

        // float fRes = pCmd2->getResult(); must not obtain command result, since it asserts
        // command state S_EXEC_OK
        // assert( fRes == 8.0 );

        cout << "******* Command 2 in ERROR." << endl;

        pInv2->RCdec(); // resource deallocation follows.

    } // complex ERROR

    cout << "******* More complex command demo EXCEPTION" << endl;
    {
        CMyCommand2div *pCmd2 = new CMyCommand2div( 0 ); // Divide by 0 is exception. No sleep.
        pCmd2->setParams( 1.0, 0.0 );

        CInvokerBackgroundCommands *pInv2 = new CInvokerBackgroundCommands( 1, // timeout 1s
                                                                            CInvokerBackgroundCommands::TOP_LINGER,
                                                                            pCmd2 );

        assert( pInv2->getInvokerState() == CInvokerBackgroundCommands::IS_S_PENDING );

        pInv2->execCmdInBgThr();

        assert( pInv2->getInvokerState() == CInvokerBackgroundCommands::IS_S_DONE );
        assert( pInv2->RCget() == 1 ); // only Invoker referencing, Bg thread is gone by now. Safe to access
                                       // Invoker command without locking, i.e. pCmd2.
        assert( pCmd2->getCommandState() == ICommand::CS_E_EXEC_EXC );
        exception *e = pCmd2->getException();
        assert( e != NULL );

        cout << "******* Command 2 exception is " << e->what() << endl;

        pInv2->RCdec(); // resource deallocation follows.

    } // complex EXCEPTION

    cout << "******* Timeout lingering demo" << endl;
    {
        // This one is the same as OK demo, but we tell the command to sleep for a time longer than timeout.

        CMyCommand2div *pCmd2 = new CMyCommand2div( 10 ); // 99 * 99 = 9801, sleep 10s.
        pCmd2->setParams( 9801.0, 99.0  );

        CInvokerBackgroundCommands *pInv2 = new CInvokerBackgroundCommands( 1, // timeout 1s
                                                                            CInvokerBackgroundCommands::TOP_LINGER,
                                                                            pCmd2 );

        assert( pInv2->getInvokerState() == CInvokerBackgroundCommands::IS_S_PENDING );

        pInv2->execCmdInBgThr();

        assert( pInv2->getInvokerState() == CInvokerBackgroundCommands::IS_E_TIMEOUT_LINGER );
        assert( pInv2->RCget() == 2 ); // Both Invoker and lingering Bg thread are referencing. Not safe to access
                                       // Invoker command, i.e. pCmd2, since Bg thread has it being executed.
        // assert( pCmd2->getException() == NULL );
        // assert( pCmd2->getCommandState() == ICommand::S_EXEC_OK );

        cout << "******* Command 2 is still running in Bg thread, we do not touch it ";

        pInv2->RCdec(); // Invoker dereferences, so RC should be 1 for the lingering Bg thread.
        assert( pInv2->RCget() == 1 );

        // If we do not wait, main() finishes and Bg thread never gets to the finish point, it is killed when
        // main exits.

        // We can implement quasi busy waiting on ref count and then go on when Bg thread dereferences it to 0.
        // It is for further study to design a solution in which this can be performed differently, e.g. waiting on
        // a condition.
        if( 1 )
        {
            cout << "******* Quasi busy waiting" << endl;
            int i = 1;

            while( pInv2->RCget() == 1 )
            {
                cout << "******* " << i++ << "s" << endl;
                CThread::sleep( 1, 0 ); // sleep 1s.
            }

            assert( pInv2->RCget() == 0 ); // Bg thread finished.
        } // if wait for Bg thread to finish.
        else
        {
            cout << "******* NOT quasi busy waiting" << endl;
            assert( pInv2->RCget() == 1 ); // Bg thread still running.
        }

    } // timeout linger demo.

    // Timeout kill demo not possible, since CThread does not allow thread to be killed.

    cout << "******* IBC end" << endl;

} // main()
